<template>
	<div class="default-main">

		<cus-table-title icon="local-cus-item" label="物品类别"/>

		<div class="classification-content">

			<el-row style="display: flex">
				<el-button icon="el-icon-Refresh" type="primary" @click="refresh">
					刷新
				</el-button>
				<el-button v-if="hasPermission('g-classification:saveClassification')" :disabled="treeDataHistory.length === 0" type="primary" @click="undo">
					<template #icon>
						<icon name="local-ant-redo"></icon>
					</template>
					撤销上一步
				</el-button>
				<el-button v-if="hasPermission('g-classification:saveClassification')" icon="el-icon-CircleCheck" style="margin-left: auto" type="primary"
				           @click="save">
					保存
				</el-button>
			</el-row>

			<el-input v-model="filterText" clearable placeholder="输入关键字进行过滤" style="margin-top: 10px"/>

			<el-row style="text-align: left;margin-top: 10px">
				<el-button icon="el-icon-Bottom" type="primary" @click="expandAll">展开全部</el-button>
				<el-button icon="el-icon-Top" type="primary" @click="collapseAll">收起全部</el-button>
				<el-button v-if="hasPermission('g-classification:saveClassification')" icon="el-icon-Plus" style="margin-left: auto" type="primary" @click="addRoot">
					添加根节点
				</el-button>
			</el-row>

			<el-tree
					ref="treeRef"
					v-loading="treeLoading"
					:allow-drag="allowDrag"
					:data="treeData"
					:default-expand-all="false"
					:expand-on-click-node="false"
					:filter-node-method="filterNode"
					:props="treeProps"
					class="cus-tree"
					draggable
					node-key="id"
					style="margin-top: 20px"
					@node-drag-start="handleDragStart"
					@node-drag-end="handleDragEnd"
					@node-drop="handleDrop"
			>
				<template v-slot="{node, data}">
					<div class="custom-tree-node">
						<div>
							{{ node.label + '（' + data.count + '）' }}
						</div>
						<div>
							<el-tooltip v-if="node.isLeaf" :disabled="!!data.id" :hide-after="0" content="请先保存分类，再分配物品" effect="dark" placement="left">
								<el-link :disabled="!data.id" :underline="false" type="primary" @click="toDistributeItem(data)">
									<icon name="local-cus-item"></icon>
									<div style="margin-left: 2px">分配物品</div>
								</el-link>
							</el-tooltip>
							<el-link v-if="hasPermission('g-classification:saveClassification')" :underline="false" style="margin-left: 20px" type="primary"
							         @click="toEdit(data)">
								<icon name="el-icon-Edit"></icon>
								<div style="margin-left: 2px">编辑</div>
							</el-link>
							<el-tooltip v-if="hasPermission('g-classification:saveClassification')" :disabled="!(node.isLeaf && data.count > 0)" :hide-after="0"
							            content="该分类已经分配了物品，不能添加下级分类" effect="dark" placement="right">
								<el-link :disabled="node.isLeaf && data.count > 0" :underline="false" style="margin-left: 20px" type="primary" @click="append(node, data)">
									<icon name="el-icon-Plus"></icon>
									<div style="margin-left: 2px">下级</div>
								</el-link>
							</el-tooltip>
							<el-tooltip v-if="hasPermission('g-classification:saveClassification')" :disabled="!(data.count > 0)" :hide-after="0"
							            content="该分类已经分配了物品，不能删除" effect="dark" placement="right">
								<div style="display: inline">
									<el-popconfirm :disabled="data.count > 0" :hide-after="0" :title="`删除【${node.label}】和所有下级节点？`" confirm-button-type="danger"
									               width="auto" @confirm="remove(node, data)">
										<template #reference>
											<el-link :disabled="data.count > 0" :underline="false" style="margin-left: 20px" type="danger">
												<icon name="el-icon-Delete"></icon>
												<div style="margin-left: 2px">删除</div>
											</el-link>
										</template>
									</el-popconfirm>
								</div>
							</el-tooltip>
						</div>
					</div>
				</template>
			</el-tree>
		</div>

		<!-- 对话框（修改） -->
		<el-dialog v-model="dialogVisible.edit" :close-on-click-modal="false" :close-on-press-escape="true" :show-close="false" append-to-body center
		           class="cus-dialog" destroy-on-close draggable title="修改类别名称" width="50%">
			<el-form ref="editFormRef" :model="classification" :rules="rules" label-width="auto" @submit.prevent>
				<el-form-item label="类别名称" prop="name">
					<el-input v-model="classification.name" clearable maxlength="100"/>
				</el-form-item>
			</el-form>
			<template #footer>
				<el-button icon="el-icon-CircleCheck" type="primary" @click="doEdit">
					确认
				</el-button>
				<el-button icon="el-icon-CircleClose" type="warning" @click="cancelEdit">
					取消
				</el-button>
			</template>
		</el-dialog>

		<!-- 对话框（分配物品） -->
		<el-dialog v-model="dialogVisible.distributeItem" :close-on-click-modal="false" :close-on-press-escape="true"
		           :title="distributingClassification.name"
		           append-to-body center class="cus-dialog" destroy-on-close draggable width="800">
			<div class="cus-center">
				<el-transfer
						v-model="selectedItemIdList"
						:data="allItemList"
						:props="transferProps"
						:titles="['全部','已选择']"
						filter-placeholder="模糊搜索"
						filterable
						target-order="push"
				>
				</el-transfer>
			</div>
			<template #footer>
				<el-button v-auth="'g-classification:saveClassificationItem'" type="primary" @click="doDistributeItem">
					保存
				</el-button>
			</template>
		</el-dialog>

	</div>
</template>

<script lang="ts" name="ItemClassification" setup>

import {reactive, ref, watch} from 'vue'
import type {FormInstance, FormRules} from 'element-plus'
import {ElMessageBox, ElTree} from 'element-plus'
import {
	getAllItemApi,
	getAllItemByClassIdApi,
	getClassificationTreeApi,
	saveClassificationApi,
	saveClassificationItemApi
} from '@/api/gameData/classification.api'
import {elTreeIterator, focusFormErrorElement, messageError, messageSuccess, treeArrayIterator} from '@/utils/commonUtil'
import {map} from 'lodash-es'
import CusTableTitle from '@/components/cus-table-title.vue'
import {hasPermission} from '@/utils/authUtil'
import type Node from 'element-plus/es/components/tree/src/model/node'
import type {TransferPropsAlias} from 'element-plus/es/components/transfer/src/transfer'
import Icon from '@/components/icon.vue'
import type {TreeOptionProps} from 'element-plus/es/components/tree/src/tree.type'

const treeRef = ref<InstanceType<typeof ElTree>>()
const editFormRef = ref<FormInstance>()

const transferProps: TransferPropsAlias = {
	key: 'id',
	label: 'itemName'
}
const treeProps = reactive<TreeOptionProps>({
	label: 'name',
	children: 'children',
	isLeaf: 'leaf',
	disabled: 'disabled'
})
const treeLoading = ref(false)
const treeData = ref<ClassificationTreeVo[]>([])
const treeDataHistory = ref<ClassificationTreeVo[][]>([])
const filterText = ref<string>()
const draggingNodeParent = ref<Node>()
const dialogVisible = reactive({
	edit: false,
	distributeItem: false
})
const classification = ref<{
	id?: number
	parentId?: number
	sort?: number
	name?: string
	originalName?: string
}>({
	id: undefined,
	parentId: undefined,
	sort: undefined,
	name: undefined,
	originalName: undefined
})
const allItemList = ref<GItem[]>([])
const selectedItemIdList = ref<number[]>([])
const selectedItemIdListOld = ref<number[]>([])
const distributingClassification = ref<ClassificationTreeVo>({})
const rules = reactive<FormRules>({
	name: [
		{required: true, message: '请输入类别名称', trigger: 'change'}
	]
})

watch(filterText, (val) => {
	treeRef.value!.filter(val)
})

const expandAll = () => {
	elTreeIterator(treeRef.value!, (node: Node) => {
		if (node.childNodes && node.childNodes.length > 0) {
			node.expanded = true
		}
	})
}

const collapseAll = () => {
	elTreeIterator(treeRef.value!, (node: Node) => {
		if (node.childNodes && node.childNodes.length > 0) {
			node.expanded = false
		}
	})
}

const handleSearch = async () => {
	treeLoading.value = true
	treeData.value = (await getClassificationTreeApi({type: 'ITEM'})).data
	treeDataHistory.value = []
	const handler = (data: ClassificationTreeVo) => {
		Reflect.set(data, 'originalName', data.name)
	}
	treeArrayIterator(treeData.value, handler)
	treeLoading.value = false
}

const filterNode = (value: string, data: ClassificationTreeVo) => {
	if (!value) return true
	return data.name!.indexOf(value) !== -1
}

const allowDrag = (node: Node) => {
	return hasPermission('g-classification:saveClassification') && !!node.data.id
}

const handleDragStart = (node: Node, ev: Event) => {
	treeDataHistory.value.push(JSON.parse(JSON.stringify(treeData.value)))
	draggingNodeParent.value = node.parent
}

const handleDragEnd = (draggingNode: Node, dropNode: Node, dropType: ElTreeDropType, ev: Event) => {
	if (JSON.stringify(treeDataHistory.value.slice(-1)[0]) === JSON.stringify(treeData.value)) {
		treeDataHistory.value.pop()
	}
}

const handleDrop = (draggingNode: Node, dropNode: Node, dropType: ElTreeDropType, ev: Event) => {
	// 落在前后
	if (dropType === 'before' || dropType === 'after') {
		let brotherNodes = dropNode.parent.childNodes
		for (const brotherNode of brotherNodes) {
			let parentId
			if (treeRef.value!.root === dropNode.parent) {
				parentId = 0
			} else {
				parentId = dropNode.parent.data.id
			}
			brotherNode.data.parentId = parentId
			brotherNode.data.sort = brotherNodes.indexOf(brotherNode) + 1
		}
	}
	// 落在内部
	else {
		let brotherNodes = dropNode.childNodes
		for (const brotherNode of brotherNodes) {
			brotherNode.data.parentId = dropNode.data.id
			brotherNode.data.sort = brotherNodes.indexOf(brotherNode) + 1
		}
	}
	for (const child of draggingNodeParent.value!.childNodes) {
		child.data.sort = draggingNodeParent.value!.childNodes.indexOf(child) + 1
	}
}

const append = (node: Node, data: ClassificationTreeVo) => {
	if (node.isLeaf && data.count! > 0) {
		messageError('该分类已经分配了物品，不能添加下级分类')
		return
	}
	treeDataHistory.value.push(JSON.parse(JSON.stringify(treeData.value)))
	const newChild = {
		id: undefined,
		parentId: data.id,
		sort: data.children!.length + 1,
		name: '新类别',
		originalName: '新类别',
		count: 0,
		children: []
	}
	if (!data.children) {
		Reflect.set(data, 'children', [])
	}
	data.children!.push(newChild)
	node.expand()
}

const toDistributeItem = (data: ClassificationTreeVo) => {
	selectedItemIdList.value = []
	distributingClassification.value = data
	getAllItemByClassIdApi(distributingClassification.value.id!).then(response => {
		selectedItemIdList.value = map(response.data, 'id')
		selectedItemIdListOld.value = map(response.data, 'id')
		dialogVisible.distributeItem = true
	})
}

const doDistributeItem = () => {
	const data: SaveClassificationItemRequest = {
		itemIdList: selectedItemIdList.value,
		itemIdListOld: selectedItemIdListOld.value,
		classificationId: distributingClassification.value.id!,
		prompt: true
	}
	saveClassificationItemApi(data).then(response => {
		if (response.data) {
			ElMessageBox.confirm(response.data, '请注意', {
				confirmButtonText: '确认继续',
				cancelButtonText: '放弃更改',
				type: 'warning',
				draggable: true
			}).then(() => {
				data.prompt = false
				saveClassificationItemApi(data).then(() => {
					messageSuccess('保存成功')
					dialogVisible.distributeItem = false
					handleSearch()
				})
			}).catch(() => {
			})
		} else {
			messageSuccess('保存成功')
			dialogVisible.distributeItem = false
			handleSearch()
		}
	})
}

const toEdit = (data: ClassificationTreeVo) => {
	treeDataHistory.value.push(JSON.parse(JSON.stringify(treeData.value)))
	classification.value = data
	dialogVisible.edit = true
}

const doEdit = () => {
	editFormRef.value!.validate(pass => {
		if (pass) {
			if (classification.value.name === classification.value.originalName) {
				treeDataHistory.value.pop()
			}
			dialogVisible.edit = false
			classification.value.originalName = classification.value.name
		} else {
			focusFormErrorElement()
		}
	})
}

const cancelEdit = () => {
	treeDataHistory.value.pop()
	dialogVisible.edit = false
	classification.value.name = classification.value.originalName
}

const remove = (node: Node, data: ClassificationTreeVo) => {
	treeDataHistory.value.push(JSON.parse(JSON.stringify(treeData.value)))
	const parent = node.parent
	const children = parent.data.children || parent.data
	const index = children.findIndex((treeVo: ClassificationTreeVo) => treeVo.$treeNodeId === data.$treeNodeId)
	children.splice(index, 1)
}

const addRoot = () => {
	treeDataHistory.value.push(JSON.parse(JSON.stringify(treeData.value)))
	treeData.value.push({
		id: undefined,
		parentId: 0,
		sort: treeData.value.length + 1,
		name: '新类别',
		originalName: '新类别',
		count: 0,
		children: []
	})
}

const save = () => {
	const classList: ClassificationInfo[] = []
	const handler = (arrayElement: ClassificationTreeVo) => {
		classList.push({
			id: arrayElement.id,
			parentId: arrayElement.parentId,
			type: 'ITEM',
			sort: arrayElement.sort,
			name: arrayElement.name,
			treeNodeId: arrayElement.$treeNodeId,
			parentTreeNodeId: arrayElement.parentId === 0 ? 0 : arrayElement.parentTreeNodeId
		})
		if (arrayElement.children && arrayElement.children.length > 0) {
			for (const child of arrayElement.children) {
				child.parentTreeNodeId = arrayElement.$treeNodeId
			}
		}
	}
	treeArrayIterator(treeData.value, handler)
	saveClassificationApi({type: 'ITEM', classList: classList}).then(() => {
		messageSuccess('保存成功')
		handleSearch()
	})
}

const undo = () => {
	if (treeDataHistory.value.length > 0) {
		treeData.value = treeDataHistory.value.pop()!
	}
}

const refresh = () => {
	if (treeDataHistory.value.length > 0) {
		ElMessageBox.confirm('未保存的更改将会丢失，确认刷新？', '请注意', {
			confirmButtonText: '确认',
			cancelButtonText: '取消',
			type: 'warning',
			draggable: true
		}).then(() => {
			handleSearch().then(() => {
				messageSuccess('已刷新')
			})
		}).catch(() => {
		})
	} else {
		handleSearch().then(() => {
			messageSuccess('已刷新')
		})
	}
}

handleSearch()
getAllItemApi().then(response => {
	allItemList.value = response.data
})

</script>

<style scoped>
.classification-content {
	width: 75%;
	margin: 0 auto;
}

.custom-tree-node {
	flex: 1;
	display: flex;
	align-items: center;
	justify-content: space-between;
	font-size: 14px;
	padding-right: 8px;
}

:deep(.el-transfer-panel) {
	width: 250px;
}

:deep(.el-transfer-panel__body) {
	height: calc(50vh);
}
</style>
